/*
 * Copyright 2022-2027 中国信息通信研究院云计算与大数据研究所
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */ 
package com.service.impl;

import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;

import com.common.calculation.InitiUtil;
import com.common.constant.AccountConstant;
import com.common.constant.PropertiesConstant;
import com.common.constant.TrancfgConstant;
import com.common.context.SpringContextUtils;
import com.mapper.QueryAccount;
import com.mapper.Transfer;
import com.pojo.Account;
import com.pojo.Branch;
import com.pojo.Trancfg;
import com.pojo.Tranlist;
import com.pojo.Tranlog;
import com.service.QueryAccountService;

@Service
@Scope("prototype")
public class QueryAccountServiceImpl implements QueryAccountService,Callable<Long> {

    private final Logger logger = LoggerFactory.getLogger(QueryAccountServiceImpl.class); 

    @Autowired
    private QueryAccount queryAccount;
    @Autowired
    DataSourceTransactionManager dataSourceTransactionManager;
    @Autowired
    TransactionDefinition transactionDefinition;
    @Autowired
    private Transfer transfer;
    @Autowired
    private PropertiesConstant propertiesConstant;
    
    private int num;
    
    private int datacfg_accnum;
    private Trancfg tf;

    private String process_id;
	/**
	 * 开始准备查询
	 * @param num
	 * @param datacfg_accnum
	 */
	public Long call() {
    	long countTime = System.currentTimeMillis();
		 for (int i = 0; i < num; i++) {
	    	logger.info("QueryAccountServiceImpl query run num " + i );
        	Date nowDate = new Date();
        	long nowMillis = nowDate.getTime();
            String accountFrom = "60"+String.valueOf((new Random().nextInt(datacfg_accnum)+1)+InitiUtil.getAccnum(datacfg_accnum));;//借方
    		Account from = null;
    		try {
        		from = transfer.readAccount(accountFrom);
			} catch (Exception e) {
				e.printStackTrace();
    			logger.error("QueryAccountServiceImpl query readAccount error {} ",e);
			}
			if(from==null) {
				logger.info("QueryAccountServiceImpl query Account from {} is null ", accountFrom);
				continue;
			}
			Tranlog tranLog = null;
          	int fromSeq = updateAccountBranchSeq(from);
          	if(fromSeq==AccountConstant.ACCOUNT_SEQ_FAILURE) {//更新网点流水号失败
          		tranLog = new Tranlog(nowDate,from.getAccount_branchid(),(int)System.currentTimeMillis(),TrancfgConstant.TRANCFG_ACCOUNTS_QUERY
            			,nowDate.getTime(),System.currentTimeMillis(),(int)(System.currentTimeMillis()-nowMillis),AccountConstant.ACCOUNT_TRAN_FAILURE,"",process_id);
    			logger.error("QueryAccountServiceImpl query updateAccountBranchSeq from {} branch {} seq {} rollback ", accountFrom,from.getAccount_branchid(),System.currentTimeMillis());
    			continue;
          	}else {
          		try {
          		 logger.info("QueryAccountServiceImpl query updateAccountBranchSeq from {} branch {} seq {} success ", accountFrom,from.getAccount_branchid(),fromSeq);
       			 if((from.getAccount_stat().equals(AccountConstant.ACCOUNT_NORMAL))) {//查询账户业务，为了保证查询一致性，需要加事务控制
       			    	TransactionStatus transactionStatus = null;
       			    	try {
       			    		transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
       			    		transfer.readAccount(accountFrom);
   	    				 	Tranlist param = new Tranlist();
   	    	        		param.setTranlist_accountid1(accountFrom);
   	    	        		param.setTranlist_accountid2(accountFrom);
   	    	        		List<Tranlist> resultList = queryAccount.queryTranlist(param);
   	    	        		int resultSize = 0;
   	    	        		if(resultList!=null) {
   	    	        			resultSize = resultList.size();
   	    	        		}
   	    	    			logger.info("QueryAccountServiceImpl query success Tranlist from {} branch {} seq {} size {} ", accountFrom,from.getAccount_branchid(),fromSeq,resultSize);
   	    	        		tranLog = new Tranlog(nowDate,from.getAccount_branchid(),fromSeq,TrancfgConstant.TRANCFG_ACCOUNTS_QUERY
   	    	            			,nowDate.getTime(),System.currentTimeMillis(),(int)(System.currentTimeMillis()-nowMillis),AccountConstant.ACCOUNT_TRAN_SUCCESS,"",process_id);
   	    	        		insertBreakPoint();
   	    	        		dataSourceTransactionManager.commit(transactionStatus);//提交
       			    	} catch (Exception e) {
       			    		try {
           				    	dataSourceTransactionManager.rollback(transactionStatus);
       						} catch (Exception e2) {
       							logger.error("QueryAccountServiceImpl query rollback error accountTo {} e {}",e);
       						}
       				    	logger.error("QueryAccountServiceImpl query rollback account {} e {}" 
       								,from.getAccount_id(),e);
           	            	tranLog = new Tranlog(nowDate,from.getAccount_branchid(),fromSeq,TrancfgConstant.TRANCFG_ACCOUNTS_QUERY
           	            			,nowDate.getTime(),System.currentTimeMillis(),(int)(System.currentTimeMillis()-nowMillis),AccountConstant.ACCOUNT_TRAN_FAILURE,"",process_id);
           	    			logger.info("QueryAccountServiceImpl query Account from {} not normal state is {} " ,accountFrom, from.getAccount_stat() );
       					}
       	            }else {
       	            	tranLog = new Tranlog(nowDate,from.getAccount_branchid(),fromSeq,TrancfgConstant.TRANCFG_ACCOUNTS_QUERY
       	            			,nowDate.getTime(),System.currentTimeMillis(),(int)(System.currentTimeMillis()-nowMillis),AccountConstant.ACCOUNT_TRAN_FAILURE,"",process_id);
       	            }
				} catch (Exception e) {
					logger.error("QueryAccountServiceImpl updateAccountBranchSeq run error brachid {} e {}" 
							,from.getAccount_branchid(),e);
				}
    			
          	}
            //插入transLog
          	insertAccountTranlog(tranLog,0);
        }
	    return System.currentTimeMillis()-countTime;
	}

	/**
     * 更新网点流水号
     * @param account
     */
    public int updateAccountBranchSeq(Account account) {
    	TransactionStatus transactionStatus = null;
    	int seq = AccountConstant.ACCOUNT_SEQ_FAILURE;
    	try {
    		transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
    		Branch branch = transfer.readTransactionalBranchSeq(account.getAccount_branchid());
        	seq = branch.getBranch_seq();
    		transfer.updateBranch(branch.getBranch_id());//更新网点流水号
    		insertBreakPoint();
        	dataSourceTransactionManager.commit(transactionStatus);//提交
		} catch (Exception e) {
			e.printStackTrace();
			try {
		    	dataSourceTransactionManager.rollback(transactionStatus);
			} catch (Exception e2) {
				logger.error("QueryAccountServiceImpl updateAccountBranchSeq rollback error account {} branchId {} e {}",account.getAccount_id(),account.getAccount_branchid(),e);
			}
	    	logger.error("QueryAccountServiceImpl updateAccountBranchSeq rollback account {} branchId {} e {}" 
					,account.getAccount_id(),account.getAccount_branchid(),e);
	    	return AccountConstant.ACCOUNT_SEQ_FAILURE;
		}
		return seq;
	}
    /**
     * 插入断点
     */
    private void insertBreakPoint() {
    	if(tf!=null&&"Y".equals(tf.getTrancfg_brkpot())) {
    		try {
				Thread.sleep(Long.parseLong(tf.getTrancfg_brktime()));
			} catch (Exception e) {
		    	logger.error("TransferServiceImpl insertBreakPoint is run error e {} ",e);
			}
    	}
	}
    /**
     * 插入tranLog
     * @param tranLog
     */
    public void insertAccountTranlog(Tranlog tranLog,int repeat) {
    	TransactionStatus transactionStatus = null;
    	boolean isSuccess = false;
    	try {
    		transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
    		transfer.insertTranlog(tranLog);
    		logger.info("QueryAccountServiceImpl insertAccountTranlog tranlog_date {} tranlog_branchid {} tranlog_branchseq {} success "
    				,tranLog.getTranlog_date(),tranLog.getTranlog_branchid(),tranLog.getTranlog_branchseq());
    		insertBreakPoint();
        	dataSourceTransactionManager.commit(transactionStatus);//提交
        	isSuccess = true;
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("QueryAccountServiceImpl insertAccountTranlog rollback tranlog_date {} tranlog_branchid {} tranlog_branchseq {} e {}" 
					,tranLog.getTranlog_date(),tranLog.getTranlog_branchid(),tranLog.getTranlog_branchseq(),e);
	    	try {
		    	dataSourceTransactionManager.rollback(transactionStatus);
			} catch (Exception e2) {
				logger.error("QueryAccountServiceImpl insertAccountTranlog rollback error accountTo {} e {}",e);
			}
		}
    	if(isSuccess) {
    		LocalTranLogServiceImpl localTranLogServiceImpl = SpringContextUtils.getBean(LocalTranLogServiceImpl.class);
        	localTranLogServiceImpl.setTranlog(tranLog);
        	localTranLogServiceImpl.start();
    	}else {
			logger.error("========>重试次数"+repeat+"===>"+tranLog.getTranlog_branchid());
    		if(repeat<propertiesConstant.getRepeat()) {
    			try {
					Thread.sleep(500);
	    	 		insertAccountTranlog(tranLog,repeat+1);
				} catch (InterruptedException e) {
					logger.error("DepositMoneyServiceImpl insertAccountTranlog Thread error tranlog_date {} tranlog_branchid {} tranlog_branchseq {} e {}" 
							,tranLog.getTranlog_date(),tranLog.getTranlog_branchid(),tranLog.getTranlog_branchseq(),e);
				}
    		}
    	}
    	
	}

	public int getNum() {
		return num;
	}

	public void setNum(int num) {
		this.num = num;
	}

	public int getDatacfg_accnum() {
		return datacfg_accnum;
	}

	public void setDatacfg_accnum(int datacfg_accnum) {
		this.datacfg_accnum = datacfg_accnum;
	}

	public String getProcess_id() {
		return process_id;
	}

	public void setProcess_id(String process_id) {
		this.process_id = process_id;
	}

	public Trancfg getTf() {
		return tf;
	}

	public void setTf(Trancfg tf) {
		this.tf = tf;
	}
    
}